
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>kafka 如何不消费重复数据？ - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>kafka 如何不消费重复数据？ - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">Kafka 是如何利用偏移量（offset）来管理消息消费，以避免重复消费的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">Kafka 使用偏移量（offset）来标识消费者在分区中已经消费的消息位置。消费者会定期提交偏移量，以记录自己已经消费的消息位置。通过精确管理偏移量，就可以避免重复消费数据。</div>
          </div>
          <div class="card-source">来源: 消息消费的偏移量管理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在管理 Kafka 消费偏移量时，自动提交和手动提交有什么关键区别和潜在风险？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">自动提交是 Kafka 提供的功能，在消费者处理消息后自动提交偏移量，但其风险在于，消息处理失败后偏移量可能已经提交。手动提交则允许消费者在消息处理成功后才提交偏移量，控制更精确，能确保消息被成功处理后再更新消费位置。</div>
          </div>
          <div class="card-source">来源: 消息消费的偏移量管理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">Kafka 的幂等性生产者是如何工作的？如何启用该功能？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">幂等性生产者通过为每条消息分配一个唯一的序列号，并结合分区信息，来确保相同的消息在同一个分区中只会被处理一次。这样即使生产者重试发送，消息也不会被重复写入。可以通过设置生产者参数 `enable.idempotence` 来开启此功能。</div>
          </div>
          <div class="card-source">来源: 消息幂等性</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">Kafka 中的 `__consumer_offsets` 主题扮演了什么角色？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">`__consumer_offsets` 是 Kafka 的一个内部主题，专门用来存储消费者组的偏移量信息。当消费者提交偏移量时，这些信息就会被写入到这个主题中。Kafka 服务器会定期清理该主题中过期的偏移量，以减少存储和管理负担。</div>
          </div>
          <div class="card-source">来源: 消息消费的偏移量管理</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">除了生产者幂等性，Kafka 还提供了什么机制来确保消息处理的原子性和一致性？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">Kafka 提供了事务（Transactions）机制。通过使用事务性生产者，可以确保一批消息要么全部成功发送，要么全部不发送，避免了部分提交的问题。只有当事务成功提交后，消息才会被持久化到日志中，从而保证了原子性和一致性。</div>
          </div>
          <div class="card-source">来源: 使用事务</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">在消费者端，可以采用什么策略来实现自定义的消息去重逻辑？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">消费者端可以实现消息去重逻辑，通过维护一个已经处理过的消息记录（可以是内存数据结构如Set，或持久化存储如数据库、Redis）。在处理每条消息前，先通过消息的唯一标识（如消息ID）检查该消息是否已被处理过，如果记录中已存在，则跳过该重复消息。</div>
          </div>
          <div class="card-source">来源: 消息去重</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">消费者组（Consumer Group）在避免重复消费方面起到了什么核心作用？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">消费者组可以确保同一个主题中的同一条消息只会被组内的某一个消费者处理。Kafka 的消费者组协调器负责将分区分配给组内消费者，并协调偏移量管理，从而从机制上避免了同一个消费者组内的多个消费者重复处理同一条消息。</div>
          </div>
          <div class="card-source">来源: 使用消费者组</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">模式</div>
          <div class="card-question">什么是时间窗口去重法，它主要适用于哪类场景？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">模式</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">时间窗口去重是一种在消费者端实现的去重策略。消费者维护一个时间窗口，如果在该时间窗口内收到具有相同标识的消息，则后续收到的消息被视为重复并丢弃。这种方式通过检查消息的时间戳是否在当前窗口内来工作，适用于对实时性要求不高的场景。</div>
          </div>
          <div class="card-source">来源: 时间窗口去重</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">为 Kafka 消息设置 key 对数据分区和避免重复消费有什么帮助？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">在消息中设置唯一的 key，可以确保具有相同 key 的消息被分配到同一个分区中。这样一来，即使同一个消息被发送多次，也只会被写入到同一个分区，保证了消息的顺序性，并帮助消费者根据分区信息来判断是否已处理过某条消息。</div>
          </div>
          <div class="card-source">来源: 数据分区和分片</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
